#include "curl_package.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common_log.h"

#ifdef _WIN32
#pragma comment(lib, "libcurl")
#endif

void Curl_Init(void) {
	curl_global_init(CURL_GLOBAL_DEFAULT); 
}

void Curl_Uninit(void) {
	curl_global_cleanup();
}

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
	size_t retcode = fread(ptr, size, nmemb, (FILE *)stream);
	return retcode;
}

static bool GetFileSizes(const char *path, long *psize) {
	FILE *pf = NULL;
	long size;

	pf = fopen(path, "rb");
	if (NULL == pf) {
		LOGERROR("fopen(%s)=NULL\n", path);
		return false;
	}
	else {
		fseek(pf, 0, SEEK_END);
		size = ftell(pf);
		fseek(pf, 0, SEEK_SET);
		fclose(pf);
		*psize = size;
		return true;
	}
}

bool Curl_Upload(const char *local_path, const char *remote_path) {
	FILE *pf_local = NULL;	
	CURL *curl = NULL;
	CURLcode curlcode;
	curl_off_t file_size;	
	bool result;

	pf_local = fopen(local_path, "rb");
	if (NULL == pf_local) {
		LOGERROR("fopen(%s)=NULL\n", local_path);
		result = false;
	}
	else {
		curl = curl_easy_init();
		if (NULL == curl) {
			LOGERROR("curl_easy_init(CURL_GLOBAL_ALL)=NULL\n");
			result = false;
		}
		else {
			GetFileSizes(local_path, (long *)&file_size);
			curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
			curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
			curl_easy_setopt(curl, CURLOPT_URL, remote_path);
			curl_easy_setopt(curl, CURLOPT_READDATA, pf_local);
			curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, file_size);
			curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);	 
			curlcode = curl_easy_perform(curl);
			if (curlcode != CURLE_OK) {
				LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
				result = false;
			}
			else {
				result = true;
			}
			curl_easy_cleanup(curl);
		}
		fclose(pf_local); 
	}
	return result;
}

static int GetRemoteName(const char *path, char *name, int size) {
	const char sep = '/';
	const char *pch = strrchr(path, sep);	
	if (NULL == pch) 
		return -1;
	strcpy(name, pch+1);
	return strlen(name);
}

bool Curl_DelFile(const char *remote_path) {
	CURL *curl = NULL;
	CURLcode curlcode;
	struct curl_slist *curlist = NULL;
	char cmd[PATH_MAX], name[PATH_MAX];
	bool result;

	curl = curl_easy_init();
	if (NULL == curl) {
		LOGERROR("curl_easy_init(CURL_GLOBAL_ALL)=NULL\n");
		result = false;
	}
	else {
		GetRemoteName(remote_path, name, sizeof(name));
		sprintf(cmd, "DELE %s", name);
		curlist = curl_slist_append(curlist, cmd);
		curl_easy_setopt(curl, CURLOPT_POSTQUOTE, curlist);
		curl_easy_setopt(curl, CURLOPT_URL, remote_path);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);	 
		curlcode = curl_easy_perform(curl);
		if (curlcode != CURLE_OK) {
			LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
			result = false;
		}
		else {
			result = true;
		}
		curl_slist_free_all(curlist);
		curl_easy_cleanup(curl);
	}
	return result;
}

static int GetRemoteDir(const char *path, char *dir, int size) {
	const char sep1 = '@', sep2 = '/';
	const char *pch1 = strchr(path, sep1);	
	if (NULL == pch1)
		return -1;
	const char *pch2 = strchr(pch1, sep2);
	if (NULL == pch2) 
		return -2;
	strcpy(dir, pch2);
	return strlen(dir);
}

bool Curl_DelEmptyDir(const char *remote_dir) {
	CURL *curl = NULL;
	CURLcode curlcode;
	struct curl_slist *curlist = NULL;
	char cmd[PATH_MAX], dir[PATH_MAX];
	bool result;

	curl = curl_easy_init();
	if (NULL == curl) {
		LOGERROR("curl_easy_init(CURL_GLOBAL_ALL)=NULL\n");
		result = false;
	}
	else {
		GetRemoteDir(remote_dir, dir, sizeof(dir));
		sprintf(cmd, "RMD %s", dir);
		curlist = curl_slist_append(curlist, cmd);
		curl_easy_setopt(curl, CURLOPT_POSTQUOTE, curlist);
		curl_easy_setopt(curl, CURLOPT_URL, remote_dir);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);	 
		curlcode = curl_easy_perform(curl);
		if (curlcode != CURLE_OK) {
			LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
			result = false;
		}
		else {
			result = true;
		}
		curl_slist_free_all(curlist);
		curl_easy_cleanup(curl);
	}
	return result;
}

struct node {
	struct node *next;
	char *name;
};

static long chunk_delbgn_cbfun(struct curl_fileinfo *finfo, void *cb_data, int remains) {
	struct node **list = (struct node **)cb_data;
	struct node *current = *list;
	struct node *newnode = (struct node *)malloc(sizeof(struct node));
	if (CURLFILETYPE_FILE == finfo->filetype) {
		//printf("------%s\n", finfo->filename);
		strcpy(current->name, finfo->filename);
		newnode->name = (char *)malloc(PATH_MAX);
		newnode->next = current;
		*list = newnode;
	}
	return CURL_CHUNK_BGN_FUNC_OK;
}

bool Curl_DelDir(const char *remote_dir) {
	struct node *list = NULL, *listmp = NULL;	
	CURL *curl = NULL;
	CURLcode curlcode;
	struct curl_slist *curlist = NULL;
	char cmd[PATH_MAX], dir[PATH_MAX];
	char remote_path[PATH_MAX];
	bool result = false;

	curl = curl_easy_init();
	if (NULL == curl) 
		LOGERROR("curl_easy_init(CURL_GLOBAL_ALL)=NULL\n");
	else {
		list = (struct node *)malloc(sizeof(struct node));
		list->name = (char *)malloc(PATH_MAX);
		list->next = NULL;
		sprintf(remote_path, "%s*", remote_dir);
		curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L);
		curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_delbgn_cbfun);
		curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, &list);
		curl_easy_setopt(curl, CURLOPT_URL, remote_path);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);	 
		curlcode = curl_easy_perform(curl);

		curl_easy_reset(curl);
		listmp = list->next;
		while (NULL != listmp) {
			sprintf(cmd, "DELE %s", listmp->name);
			curlist = curl_slist_append(curlist, cmd);
			listmp = listmp->next;
		}
		GetRemoteDir(remote_dir, dir, sizeof(dir));
		sprintf(cmd, "RMD %s", dir);
		curlist = curl_slist_append(curlist, cmd);
		curl_easy_setopt(curl, CURLOPT_POSTQUOTE, curlist);
		curl_easy_setopt(curl, CURLOPT_URL, remote_dir);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);	 
		curlcode = curl_easy_perform(curl);
		if (curlcode != CURLE_OK) 
			LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
		else 
			result = true;
		curl_slist_free_all(curlist);

		while (NULL != list) {
			listmp = list;
			list = list->next;
			free(listmp->name);
			free(listmp);
		}
		curl_easy_cleanup(curl);
	}
	return result;
}
 
typedef struct {
	const char *m_path;
	FILE *m_stream;
} t_File;
 
static size_t write_callback(void *buffer, size_t size, size_t nmemb, void *stream) {
	t_File *out = (t_File *)stream;
	size_t write_ret;

	if (NULL == buffer || NULL == stream || NULL == out->m_path) {
		return -1;
	}
	else {
		if (NULL == out->m_stream) {
			out->m_stream = fopen(out->m_path, "wb");
			if (NULL == out->m_stream) {
				return -1;
			}
		}
		write_ret = fwrite(buffer, size, nmemb, out->m_stream);
		return write_ret;
	}
}
 
bool Curl_Dwonload(const char *remote_path, const char *local_path, int timeout_ms) {
	t_File local_file;
	CURL *curl = NULL;
	CURLcode curlcode;
	bool result;
 
	curl = curl_easy_init();
	if (NULL == curl) {
		LOGERROR("curl_easy_init(CURL_GLOBAL_DEFAULT)=NULL\n");
		result = false;
	}
	else {
		local_file.m_path = local_path;
		local_file.m_stream = NULL;
		curl_easy_setopt(curl, CURLOPT_URL, remote_path);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &local_file);
		curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout_ms);  	
		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);				
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
		curlcode = curl_easy_perform(curl);
		if (curlcode != CURLE_OK) {
			LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
			result = false;
		}
		else {
			result = true;
		}
		curl_easy_cleanup(curl);
	}
	if (NULL != local_file.m_stream)
		fclose(local_file.m_stream);
	
	return result;
}

struct wildcard_data {
	FILE *output;
	const char *local_dir;
	const void *remain_data;
	int (*filter)(struct curl_fileinfo *finfo, int remains);
	int (*CombPath)(struct curl_fileinfo *finfo, const char *ldir, const void *remaindata, char *lpath, int lsize);
};

static long chunk_bgn_cbfun(struct curl_fileinfo *finfo, void *cb_data, int remains) {
	struct wildcard_data *data = (struct wildcard_data *)cb_data;
	char local_path[PATH_MAX];
	int comb_ret;

	if (NULL != data->filter && 0 >= data->filter(finfo, remains))
		return CURL_CHUNK_BGN_FUNC_SKIP;

	if (CURLFILETYPE_FILE == finfo->filetype) {
		if (NULL == data->local_dir)
			strcpy(local_path, finfo->filename);
		else {
#ifdef _WIN32
			sprintf(local_path, "%s\\%s", data->local_dir, finfo->filename);
#else
			sprintf(local_path, "%s/%s", data->local_dir, finfo->filename);
#endif // _WIN32
		}
		if (NULL != data->CombPath) {
			comb_ret = data->CombPath(finfo, data->local_dir, data->remain_data, local_path, sizeof(local_path));
			if (0 >= comb_ret)
				return CURL_CHUNK_BGN_FUNC_SKIP;
		}
		data->output = fopen(local_path, "wb");
		if(NULL == data->output) 
			return CURL_CHUNK_BGN_FUNC_FAIL;
	}
	return CURL_CHUNK_BGN_FUNC_OK;
}

static long chunk_end_cbfun(void *cb_data) {
	struct wildcard_data *data = (struct wildcard_data *)cb_data;
	if(data->output) {
		fclose(data->output);
		data->output = 0x0;
	}
	return CURL_CHUNK_END_FUNC_OK;
}

static size_t write_cbfun(char *buff, size_t size, size_t nmemb, void *cb_data) {
	struct wildcard_data *data = (struct wildcard_data *)cb_data;
	size_t written = 0;
	if(data->output)
		written = fwrite(buff, size, nmemb, data->output);
	return written;
}

bool Curl_DownloadWildcard(const char *remote_path, const char *local_dir, 
						   int (*Filter)(struct curl_fileinfo *finfo, int remains), 
						   int (*CombPath)(struct curl_fileinfo *finfo, const char *ldir, const void *remaindata, char *lpath, int lsize), 
						   int timeout_ms, const void *remaindata) {
	CURL *curl = NULL;
	CURLcode curlcode;
	struct wildcard_data data = {NULL, local_dir, remaindata, Filter, CombPath};
	bool result = false;

	curl = curl_easy_init();
	if (NULL == curl) {
		LOGERROR("curl_easy_init(CURL_GLOBAL_DEFAULT)=NULL\n");
	}
	else {
		curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L);
		curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn_cbfun);
		curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end_cbfun);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cbfun);
		curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, &data);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
		curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout_ms);  	
		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);				
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); 
		curl_easy_setopt(curl, CURLOPT_URL, remote_path);
		curlcode = curl_easy_perform(curl);
		if (curlcode != CURLE_OK) {
			LOGERROR("curl_easy_perform(0x%p)!=CURLE_OK, {%d,%s}\n", curl, curlcode, curl_easy_strerror(curlcode));
		}
		else {
			result = true;
		}
		curl_easy_cleanup(curl);
	}
	return result;
}

#if 0
#include "transcode.h"
int Filter(struct curl_fileinfo *finfo, int remains) {
	char *gbkname = (char *)malloc(256);
	UTF8ToGBK(finfo->filename, gbkname, 256);
	printf("%3d %10luB %s %s\n", remains, (unsigned long)finfo->size, finfo->b_data, gbkname);
	free(gbkname);
	if (finfo->size > 50*240) {
		return 1;
	}
	return 0;
}

int CombPath(struct curl_fileinfo *finfo, const char *rpath, const char *ldir, char *lpath, int lsize) {
	char *gbkname = (char *)malloc(256);
	UTF8ToGBK(finfo->filename, gbkname, 256);
	if (NULL == ldir)
		strcpy(lpath, gbkname);
	else {
		sprintf(lpath, "%s\\%s_test.jpg", ldir, gbkname);
	}	
	free(gbkname);
	return 1;
}

int main(void) {
	Curl_Init();
	//int a = Curl_DelFile("ftp://ssyang:ssyang@192.168.1.66/photos/123/123.txt");
	//int b = Curl_DelEmptyDir("ftp://ssyang:ssyang@192.168.1.66/photos/123/");
	//while (1) 
	int c = Curl_DelDir("ftp://ssyang:ssyang@192.168.1.66/photos/123/");
	//bool ok = Curl_DownloadWildcard("ftp://AnQuanDai:anquandai@127.0.0.1/*", "5", Filter, CombPath, 3000);

	//char local_path[] = "E:\\6_FuJian\\FuZhou_Camera\\bin\\11.jpg";
	//char remote_path[] = "ftp://AnQuanDai:anquandai@127.0.0.1/11111.jpg";
	//char remote_path[] = "ftp://AnQuanDai:anquandai@127.0.0.1/11111.jpg";
	//bool ok = Curl_Dwonload(remote_path, local_path, 3000);
	//bool ok = Curl_Upload(local_path, remote_path);
	//printf("ok=%d\n", ok);

	//char local_path2[] = "E:\\abcdefg.jpg";
	//char remote_path2[] = "ftp://ssyang:ssyang@192.168.1.66/123.txt";
	//char remote_path2[] = "http://easyread.ph.126.net/m01ZnaivFu8yR-lVjO62vg==/7917012585080905357.jpg";
	//Curl_Dwonload(remote_path2, local_path2);
	Curl_Uninit();

	return 0;
}
#endif

